//Program by:
//Ben Schmidt - bjs778 - 11054018
//Mike Nimeck - mfn028 - 11053045
//Jeff Huang - jlh105 - 11050226

// Flocking update vertex shader
#version 330

// Position and velocity inputs
layout (location = 0) in vec3 flock_position;
layout (location = 1) in vec3 flock_velocity;
layout (location = 2) in vec3 flock_color;
layout (location = 3) in vec3 flock_colorVelocity;

// Outputs (via transform feedback)
out vec3 position_out;
out vec3 velocity_out;
out vec3 color_out;
out vec3 colorVelocity_out;

// TBOs containing the position and velocity of other flock members
uniform samplerBuffer tex_positions;
uniform samplerBuffer tex_velocities;
uniform samplerBuffer tex_colors;
uniform samplerBuffer tex_colorVelocities;

// Parameters...
// This has to match the app's view of the world - no default is given here.
uniform int num_particles;
// These all have defaults. In the example application, these aren't changed.
// Just edit these and rerun the application. It's certainly possible to change
// these parameters at run time by hooking the uniforms up in the application.

// Defaults (this will make a ball of flocking around cursor)
 uniform float rule1_weight = 0.17;
 uniform float rule2_weight = 0.01;
 uniform float rule3_weight = 0.75;
 uniform float colorRule1_weight = 0.17;
 uniform float colorRule2_weight = 0.10;
 uniform float colorRule3_weight = 0.75;
 uniform float closest_allowed_dist = 200.0;
 uniform float closest_allowed_color = 0.025;
 
 uniform float damping_coefficient = 0.99999;

 
// Not Defaults (this will make a donut around cursor)
// uniform float rule1_weight = 0.0;
// uniform float rule2_weight = 0.0;
// uniform float damping_coefficient = 0.99999;
// uniform float closest_allowed_dist = 1.0;

// Time varying uniforms
uniform vec3 goal;
uniform vec3 colorGoal;
uniform float timestep;

// The two per-member rules
vec3 rule1(vec3 my_position, vec3 my_velocity, vec3 their_position, vec3 their_velocity, float closest_dist)
{
	vec3 d = my_position - their_position;
	if (dot(d, d) < closest_dist)
		return d;
	return vec3(0.0);
}

vec3 rule2(vec3 my_position, vec3 my_velocity, vec3 their_position, vec3 their_velocity)
{
	vec3 d = their_position - my_position;
	vec3 dv = their_velocity - my_velocity;
	return dv / (dot(d, d) + 10.0);
}

void main(void)
{
	vec3 acceleration = vec3(0.0);
	vec3 center = vec3(0.0);
	vec3 new_velocity;
	
	vec3 colorAccelleration = vec3(0.0);
	vec3 colorCenter = vec3(0.0);
	vec3 new_colorVelocity;
	
	int i;

	// Apply rules 1 and 2 for my member in the flock (based on all other
	// members)
	for (i = 0; i < num_particles; i++) 
	{
		if (i != gl_VertexID) 
		{
			//calculate position and velocity
			vec3 their_position = texelFetch(tex_positions, i).xyz;
			vec3 their_velocity = texelFetch(tex_velocities, i).xyz;

			acceleration += 
				rule1(
				flock_position, 
				flock_velocity, 
				their_position, 
				their_velocity,
				closest_allowed_dist) * rule1_weight;
			
			acceleration += 
			rule2(
			flock_position, 
			flock_velocity, 
			their_position, 
			their_velocity) * rule2_weight;
		
			center += their_position;
			
			//calculate color and colorVelocity
			vec3 their_color = texelFetch(tex_colors, i).xyz;
			vec3 their_colorVelocity = texelFetch(tex_colorVelocities, i).xyz;

			colorAccelleration += 
				rule1(
				flock_color, 
				flock_colorVelocity, 
				their_color, 
				their_colorVelocity,
				closest_allowed_color) * colorRule1_weight;
			
			colorAccelleration += 
			rule2(
			flock_color, 
			flock_colorVelocity, 
			their_color, 
			their_colorVelocity) * colorRule2_weight;
		
			colorCenter += their_color;
		}
	}
	
	
	
	// Also accellerate towards the goal (rule 3)     
	// (1.0f to show follow, 0.025f normal)
	acceleration += normalize(goal - flock_position) * rule3_weight;//0.025;
	
	// Also... color-accellerate towards the... color-goal (rule 3)
	colorAccelleration += normalize (colorGoal - flock_color) * colorRule3_weight;
	
	// Update position based on prior velocity and timestep
	position_out = flock_position + flock_velocity * timestep;
	
	// Update color based on prior colorVelocity and timestep
	color_out = flock_color + flock_colorVelocity * timestep;
	
	// Update velocity based on calculated acceleration
	acceleration = normalize(acceleration) * min(length(acceleration), 10.0);
	new_velocity = flock_velocity * damping_coefficient + acceleration * timestep;
	
	// Update color-velocity based on calculated color-acceleration
	colorAccelleration = normalize(colorAccelleration) * min(length(colorAccelleration), 0.1);
	new_colorVelocity = flock_colorVelocity * damping_coefficient + colorAccelleration * timestep;
	
	// Hard clamp speed (mag(velocity) to 10 to prevent insanity
	if (length(new_velocity) > 10.0)
		new_velocity = normalize(new_velocity) * 10.0;
	velocity_out = new_velocity;
	
	//Normalize color so all are in 0-1 range
	if (length(new_colorVelocity) > 0.005f)
		new_colorVelocity = normalize(new_colorVelocity) * 0.005f;
	colorVelocity_out = new_colorVelocity;
	
	// Write position (not strictly necessary as we're capturing user defined
	// outputs using transform feedback)
	gl_Position = vec4(flock_position * 0.1, 1.0);
}
